package com.g2.rpc.rpc.client;

import com.g2.rpc.config.RegistryConfig;
import com.g2.rpc.registry.NotifyListener;
import com.g2.rpc.registry.RegistryService;
import com.g2.rpc.rpc.Invoker;
import com.g2.rpc.rpc.LoadBalance;
import com.g2.rpc.rpc.Protocol;
import com.g2.rpc.rpc.RpcInvocation;
import com.g2.rpc.util.SpiUtil;
import org.apache.log4j.Logger;

import java.lang.reflect.Field;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 描述:
 * ${DESCRIPTION}
 *
 * @author lingjiu.kong
 * @version 1.0
 * @date 2021-03-05 22:58
 */
public class ClusterInvoker implements Invoker {

    private static Logger log = Logger.getLogger("g2rpc");


    private LoadBalance loadBalance;

    /**
     * 代表这个服务能够调用的所有实例
     */
    Map<URI, Invoker> invokers = new ConcurrentHashMap<>();


    public ClusterInvoker(Field declaredField, String loadBalance, RegistryConfig registryConfig) {
        this.loadBalance = SpiUtil.load(LoadBalance.class, loadBalance);
        log.info("loadBalance: " + this.loadBalance);
        String serviceName = declaredField.getType().getName();
        try {
            URI registryUri = new URI(registryConfig.getAddress());
            RegistryService registry = SpiUtil.load(RegistryService.class, registryUri.getScheme());
            registry.init(registryUri);
            registry.subscribe(serviceName, new NotifyListener() {
                @Override
                public void notify(Set<URI> uris) {
                    log.info("更新前的服务invoker信息" + invokers);
                    // 剔除 - 创建好的invoker，是不是存在于最小的 实例里面
                    for (URI uri : invokers.keySet()) {
                        if (!uris.contains(uri)) {
                            invokers.remove(uri);
                        }
                    }

                    // 新增 - 意味新建一个invoker
                    for (URI uri : uris) {
                        if (!invokers.containsKey(uri)) {
                            // 意味着有一个服务实例
                            Protocol protocol = SpiUtil.load(Protocol.class, uri.getScheme());
                            Invoker invoker = protocol.reference(uri); // invoke 代表一个长连接
                            // 保存起来
                            invokers.putIfAbsent(uri, invoker);
                        }
                    }
                    log.info("更新后的服务invoker信息" + invokers);
                }
            });


        } catch (URISyntaxException e) {
            log.error("", e);
        }
    }

    @Override
    public Object invoke(RpcInvocation rpcInvocation) throws Exception{
        return loadBalance.select(invokers).invoke(rpcInvocation);
    }


}
